

package com.junjie.index12306.biz.payservice.handler;

import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.junjie.index12306.biz.payservice.common.enums.PayChannelEnum;
import com.junjie.index12306.biz.payservice.common.enums.PayTradeTypeEnum;
import com.junjie.index12306.biz.payservice.config.AliPayProperties;
import com.junjie.index12306.biz.payservice.dto.base.AliPayRequest;
import com.junjie.index12306.biz.payservice.dto.base.PayRequest;
import com.junjie.index12306.biz.payservice.dto.base.PayResponse;
import com.junjie.index12306.biz.payservice.handler.base.AbstractPayHandler;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import com.junjie.index12306.framework.starter.common.toolkit.BeanUtil;
import com.junjie.index12306.framework.starter.convention.exception.ServiceException;
import com.junjie.index12306.framework.starter.designpattern.strategy.AbstractExecuteStrategy;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

/**
 * 阿里Native支付组件
 * 还拓展别的 比如 阿里扫码支付组件等
 *
 * 
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AliPayNativeHandler extends AbstractPayHandler implements AbstractExecuteStrategy<PayRequest, PayResponse> {

    private final AliPayProperties aliPayProperties;

    /**
     * 统一PC场景下收单下单并支付页面接口文档地址
     * https://opendocs.alipay.com/open/59da99d0_alipay.trade.page.pay?pathHash=e26b497f&scene=22&ref=api
     */
    @SneakyThrows(value = AlipayApiException.class)
    @Override
    // 重试注解，在应用程序中，由于一些网络等不可预知的问题，我们的程序或者接口会失败，比如调用一个第三方的接口获取数据失败了，这时就需要重试机制，比如延时3S后重试、间隔不断增加重试等，而这些机制完全不需要你自己去实现，全部交给Spring Retry吧
    @Retryable(value = ServiceException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 1.5))
    public PayResponse pay(PayRequest payRequest) {
        AliPayRequest aliPayRequest = payRequest.getAliPayRequest();
        // 1、阿里配置类属性复制阿里官方配置类 并用该官方配置类 创建阿里客户端
        AlipayConfig alipayConfig = BeanUtil.convert(aliPayProperties, AlipayConfig.class);
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
        // 2、构造阿里model业务请求参数对象
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        model.setOutTradeNo(aliPayRequest.getOrderSn()); // 商户订单号
        model.setTotalAmount(aliPayRequest.getTotalAmount().toString()); // 订单总金额
        model.setSubject(aliPayRequest.getSubject()); // 订单标题
        model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码，与支付宝签约的产品码名称。注：目前电脑支付场景下仅支持FAST_INSTANT_TRADE_PAY
        // 3、构造阿里request对象 设置回调地址和请求参数
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(aliPayProperties.getNotifyUrl());
        request.setBizModel(model);
        try {
            // 4、使用阿里客户端发送请求
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            log.info("发起支付宝支付，订单号：{}，子订单号：{}，订单请求号：{}，订单金额：{} \n调用支付返回：\n\n{}\n",
                    aliPayRequest.getOrderSn(),
                    aliPayRequest.getOutOrderSn(),
                    aliPayRequest.getOrderRequestId(),
                    aliPayRequest.getTotalAmount(),
                    JSONObject.toJSONString(response));
            // 5、判断调用支付是否成功
            if (!response.isSuccess()) {
                throw new ServiceException("调用支付宝发起支付异常");
            }
            return new PayResponse(StrUtil.replace(StrUtil.replace(response.getBody(), "\"", "'"), "\n", ""));
        } catch (AlipayApiException ex) {
            throw new ServiceException("调用支付宝支付异常");
        }
    }

    @Override
    public String mark() {
        return StrBuilder.create()
                .append(PayChannelEnum.ALI_PAY.name())
                .append("_")
                .append(PayTradeTypeEnum.NATIVE.name())
                .toString();
    }

    @Override
    public PayResponse executeResp(PayRequest requestParam) {
        return pay(requestParam);
    }
}
